"""
Validators following SOLID principles.
"""

import re
from typing import Optional, List
from abc import ABC, abstractmethod


class BaseValidator(ABC):
    """Base validator interface."""
    
    @abstractmethod
    def validate(self, value: str) -> bool:
        """Validate a value."""
        pass
    
    @abstractmethod
    def get_error_message(self) -> str:
        """Get validation error message."""
        pass


class EmailValidator(BaseValidator):
    """Email address validator."""
    
    EMAIL_PATTERN = re.compile(
        r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    )
    
    def validate(self, email: str) -> bool:
        """Validate email address."""
        if not email or not isinstance(email, str):
            return False
        return bool(self.EMAIL_PATTERN.match(email.strip()))
    
    def get_error_message(self) -> str:
        """Get email validation error message."""
        return "Invalid email address format"


class PasswordValidator(BaseValidator):
    """Password strength validator."""
    
    def __init__(
        self,
        min_length: int = 8,
        require_uppercase: bool = True,
        require_lowercase: bool = True,
        require_digits: bool = True,
        require_special: bool = True,
        special_chars: str = "!@#$%^&*()_+-=[]{}|;:,.<>?"
    ):
        self.min_length = min_length
        self.require_uppercase = require_uppercase
        self.require_lowercase = require_lowercase
        self.require_digits = require_digits
        self.require_special = require_special
        self.special_chars = special_chars
    
    def validate(self, password: str) -> bool:
        """Validate password strength."""
        if not password or not isinstance(password, str):
            return False
        
        # Check minimum length
        if len(password) < self.min_length:
            return False
        
        # Check uppercase requirement
        if self.require_uppercase and not any(c.isupper() for c in password):
            return False
        
        # Check lowercase requirement
        if self.require_lowercase and not any(c.islower() for c in password):
            return False
        
        # Check digits requirement
        if self.require_digits and not any(c.isdigit() for c in password):
            return False
        
        # Check special characters requirement
        if self.require_special and not any(c in self.special_chars for c in password):
            return False
        
        return True
    
    def get_error_message(self) -> str:
        """Get password validation error message."""
        requirements = [f"at least {self.min_length} characters"]
        
        if self.require_uppercase:
            requirements.append("uppercase letter")
        if self.require_lowercase:
            requirements.append("lowercase letter")
        if self.require_digits:
            requirements.append("digit")
        if self.require_special:
            requirements.append("special character")
        
        return f"Password must contain {', '.join(requirements)}"
    
    def get_strength_score(self, password: str) -> int:
        """Get password strength score (0-100)."""
        if not password:
            return 0
        
        score = 0
        
        # Length score (max 25 points)
        score += min(25, len(password) * 2)
        
        # Character variety (max 75 points)
        if any(c.isupper() for c in password):
            score += 15
        if any(c.islower() for c in password):
            score += 15
        if any(c.isdigit() for c in password):
            score += 15
        if any(c in self.special_chars for c in password):
            score += 15
        
        # Bonus for length > 12
        if len(password) > 12:
            score += 15
        
        return min(100, score)


class PhoneValidator(BaseValidator):
    """Phone number validator."""
    
    def __init__(self, country_code: Optional[str] = None):
        self.country_code = country_code
        # Basic international phone pattern
        self.phone_pattern = re.compile(r'^\+?[1-9]\d{1,14}$')
        
        # Country-specific patterns
        self.country_patterns = {
            'US': re.compile(r'^\+?1?[2-9]\d{2}[2-9]\d{2}\d{4}$'),
            'CN': re.compile(r'^\+?86?1[3-9]\d{9}$'),
            'UK': re.compile(r'^\+?44[1-9]\d{8,9}$'),
        }
    
    def validate(self, phone: str) -> bool:
        """Validate phone number."""
        if not phone or not isinstance(phone, str):
            return False
        
        # Remove spaces, dashes, and parentheses
        cleaned_phone = re.sub(r'[\s\-\(\)]', '', phone)
        
        # Use country-specific pattern if available
        if self.country_code and self.country_code in self.country_patterns:
            return bool(self.country_patterns[self.country_code].match(cleaned_phone))
        
        # Use general international pattern
        return bool(self.phone_pattern.match(cleaned_phone))
    
    def get_error_message(self) -> str:
        """Get phone validation error message."""
        if self.country_code:
            return f"Invalid {self.country_code} phone number format"
        return "Invalid phone number format"


class URLValidator(BaseValidator):
    """URL validator."""
    
    URL_PATTERN = re.compile(
        r'^https?://'  # http:// or https://
        r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|'  # domain...
        r'localhost|'  # localhost...
        r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'  # ...or ip
        r'(?::\d+)?'  # optional port
        r'(?:/?|[/?]\S+)$', re.IGNORECASE
    )
    
    def validate(self, url: str) -> bool:
        """Validate URL."""
        if not url or not isinstance(url, str):
            return False
        return bool(self.URL_PATTERN.match(url.strip()))
    
    def get_error_message(self) -> str:
        """Get URL validation error message."""
        return "Invalid URL format"


# Validator instances
email_validator = EmailValidator()
password_validator = PasswordValidator()
phone_validator = PhoneValidator()
url_validator = URLValidator()